from fastapi import APIRouter, Query, UploadFile, File, HTTPException, Form
from fastapi.responses import JSONResponse, StreamingResponse, RedirectResponse
import sqlite3
import mysql.connector
import os
import hashlib
import datetime
import shutil
import json
import logging
from typing import List, Optional, Dict, Any
from utils import nas_sqlite_dirdb_get, get_mysql_conn, CONFIG
from system import getnodeip
from utils import nas_add_file
# 使用统一的日志配置
from logging_config import get_logger

# 获取模块logger
logger = get_logger('file_operations')
# 创建文件操作的路由
file_router = APIRouter()

# 文件操作接口

# 从fidcache模块导入获取文件缓存的函数
from fidcache import get_fid_cache, get_fid_from_mysql

# 预览接口
@file_router.get("/v1/oss/view")
async def file_view(
    fid: str = Query(...),
    corpid: str = Query(...),
    subid: str = Query(...),
    uid: str = Query(...)
):
    try:        
        file_info = get_fid_cache(fid, corpid)
        if not file_info:
            return JSONResponse(content={"code": 404, "msg": "文件不存在"}) 
        fpath = file_info.get('fpath', '')
        if not fpath:
            return JSONResponse(content={"code": 400, "msg": "文件路径无效"})
        parts = fpath.split(':', 1)
        if len(parts) != 2:
            return JSONResponse(content={"code": 400, "msg": "文件路径格式错误"})
        nodeid = parts[0]
        edge_path = parts[1]        
        # 从配置中查找特定nodeid的节点信息
        # 使用getnodeip函数获取节点信息
        node_info = getnodeip(nodeid)        
        if not node_info:
            return JSONResponse(content={"code": 500, "msg": "无法获取节点信息"})        
        
        # 使用netip和端口构建重定向URL
        ip = node_info['netip']
        port = node_info['port']
        mimeid = file_info.get('mimeid', 0)        
        # 构建重定向URL
        redirect_url = f"http://{ip}:{port}/view?fid={fid}&id={mimeid}-{edge_path}"        
        # 返回302重定向
        return RedirectResponse(url=redirect_url, status_code=302)
        
    except Exception as e:
        print(f"文件预览失败: {e}")
        return JSONResponse(content={"code": 500, "msg": "内部错误"})
    
# 上传元数据接口
@file_router.get("/v1/oss/file")
async def file_operations(
    action: str = Query(...),
    fid: Optional[str] = Query(None),
    uid: Optional[str] = Query(None),
    path: Optional[str] = Query(None),
    name: Optional[str] = Query(None),
    size: Optional[str] = Query(None),
    corpid: Optional[str] = Query(None),
    subid: Optional[str] = Query(None),
    groupid: Optional[str] = Query(None),
    newname: Optional[str] = Query(None),
    newpath: Optional[str] = Query(None),
    oldname: Optional[str] = Query(None),
    oldpath: Optional[str] = Query(None),
    nodeid: Optional[str] = Query(None),
    pid: Optional[str] = Query(None),
    filetype: Optional[str] = Query(None),
    key: Optional[str] = Query(None),
    startnum: Optional[int] = Query(1),
    pagenum: Optional[int] = Query(25),
    admin: Optional[int] = Query(0),
    groupids: Optional[str] = Query(None),
    sort: Optional[int] = Query(1),
    orderby: Optional[int] = Query(1),
    stype: Optional[int] = Query(0)
):
    if action == "uploadmeta" or action == "uploadfile":
        # 上传元数据
        if not fid or not uid or not path or not name or not size or not corpid or not subid:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
        
        # 连接数据库 - 使用路径参数，确保每个目录有自己的数据库
        sqlite_conn = nas_sqlite_dirdb_get(corpid, path)
        if not sqlite_conn:
            return JSONResponse(content={"code": 500, "msg": "数据库连接失败"})
        
        try:
            cursor = sqlite_conn.cursor()            
            # 检查文件是否已存在
            cursor.execute("SELECT name,delflag FROM file_info WHERE fid = ?", (fid))
            existing_file = cursor.fetchone()            
            if existing_file:                
                if existing_file and existing_file[1] == 0: 
                    return JSONResponse(content={"code": 200, "msg": "file is correct"})
                else:                  
                    fidcache = get_fid_cache(fid, corpid)                    
                    fpath = fidcache['fpath']
                    parts = fpath.split(':', 1)                   
                    nodeid = parts[0]                    
                    node_info = getnodeip(nodeid)
                    if node_info:
                        upload_url = f"http://{node_info['netip']}:{node_info['port']}/file?action=upload&fid={fid}&path={path}&size={size}&uid={uid}"
                        return JSONResponse(content={"code": 302, "msg": "continue upload", "url": upload_url})   
            # 确定文件类型
            file_type = 1  # 默认文件类型
            if name.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp')):
                file_type = 2  # 图片
            elif name.lower().endswith(('.doc', '.docx', '.pdf', '.txt', '.xls', '.xlsx', '.ppt', '.pptx')):
                file_type = 3  # 文档
            elif name.lower().endswith(('.mp3', '.wav', '.flac', '.aac')):
                file_type = 4  # 音频
            elif name.lower().endswith(('.zip', '.rar', '.7z', '.tar', '.gz')):
                file_type = 5  # 压缩包
            elif name.lower().endswith(('.mp4', '.avi', '.mkv', '.mov', '.wmv')):
                file_type = 7  # 视频
            
            # 插入文件元数据
            current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            cursor.execute("""
                INSERT INTO file_info (fid, name, ctime, fsize, cursize, filetype, uid, subid, corpid, delflag)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (fid, name, current_time, int(size), 0, file_type, uid, subid, corpid, 5))
            
            sqlite_conn.commit()
            
            # 更新到MySQL
            mysql_conn = get_mysql_conn()
            if mysql_conn:
                try:
                    mysql_cursor = mysql_conn.cursor()
                    mysql_cursor.execute(f"""
                            INSERT INTO file_info_{corpid} (fid, NAME, ctime, fsize, cursize, filetype, uid, subid, delflag)
                            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
                        """, (fid, name, current_time, int(size), 0, file_type, uid, subid, 5))
                    mysql_conn.commit()
                except Exception as e:
                    logger.error(f"MySQL更新失败: {e}")
                finally:
                    try:
                        mysql_conn.close()
                    except Exception as e:
                        logger.warning(f"关闭MySQL连接失败: {e}")
            
            # 返回上传地址
            edge_info = get_best_edgesrv()
            upload_url = f"http://{edge_info['ip']}:{edge_info['port']}/file?action=upload&fid={fid}&path={path}&type={file_type}&size={size}&cursize=0&uid={uid}"
            return RedirectResponse(url=upload_url, status_code=302)
        except Exception as e:
            print(f"上传元数据失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
        finally:
            sqlite_conn.close()
    
    elif action == "search":
        # 文件搜索 - 查询MySQL中的file_info_corpid表
        if not corpid or not subid or not uid:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
        
        try:
            # 构建表名 file_info_{corpid}
            table_name = f"file_info_{corpid}"          
            query = f"SELECT fid, name, ctime, fsize, fsource, filetype, mimeid, resolvepower, codingformat, coderate, codetime FROM {table_name} WHERE 1=1"
            params = []  
            if filetype and filetype != "5":  # 5表示所有类型
                query += " AND filetype = %s"
                params.append(filetype)            
            # 添加关键字条件
            if key:
                if stype == 1:  # 搜索fid
                    query += " AND fid LIKE %s"
                    params.append(f"%{key}%")
                else:  # 搜索文件名
                    query += " AND name LIKE %s"
                    params.append(f"%{key}%")            
            # 添加权限条件
            if admin == 0:
                # 普通用户只能搜索自己的文件和公共空间的文件
                query += " AND (uid = %s or pid=0 )"
                params.append(uid)               
            
            # 添加部门条件
            if groupids:
                # 预留功能，暂不实现
                pass
            
            # 添加排序
            sort_fields = {1: "name", 2: "fsize", 3: "ctime"}
            sort_field = sort_fields.get(sort, "name")
            order = "ASC" if orderby == 1 else "DESC"
            query += f" ORDER BY {sort_field} {order}"
            
            # 添加分页
            offset = (startnum - 1) * pagenum
            query += " LIMIT %s OFFSET %s"
            params.append(pagenum)
            params.append(offset)
            
            # 执行查询 - 使用query_mysql函数从MySQL查询
            results = query_mysql(query, params=params, fetchone=False)
            
            if results is None:
                return JSONResponse(content={"code": 500, "msg": "数据库查询失败"})
            
            # 获取总数
            count_query = f"SELECT COUNT(*) FROM {table_name} WHERE 1=1"
            count_params = []
            # 复制其他条件到计数查询
            if filetype and filetype != "5":
                count_query += " AND filetype = %s"
                count_params.append(filetype)
            if key:
                if stype == 1:
                    count_query += " AND fid LIKE %s"
                    count_params.append(f"%{key}%")
                else:
                    count_query += " AND name LIKE %s"
                    count_params.append(f"%{key}%")
            if admin == 0:
                count_query += " AND (subid = %s OR fpath LIKE %s)"
                count_params.append(subid)
                count_params.append("%/公共空间%")
            
            count_result = query_mysql(count_query, params=count_params, fetchone=True)
            totalnum = count_result[0] if count_result else 0
            
            # 格式化结果
            data = []
            for row in results:
                data.append({
                    "fid": row[0],
                    "name": row[1],
                    "ctime": row[2],
                    "fsize": str(row[3]),
                    "fsource": row[4] or "0",
                    "ftype": str(row[5]),
                    "mimeid": str(row[6] or 0),
                    "resolvepower": row[7] or "",
                    "codingformat": row[8] or "",
                    "coderate": row[9] or "",
                    "codetime": row[10] or ""
                })
            
            return JSONResponse(content={
                "code": 200,
                "totalnum": totalnum,
                "num": len(data),
                "data": data
            })
        except Exception as e:
            print(f"文件搜索失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
    
    elif action == "rename":
        # 文件重命名
        if not corpid or not subid or not uid or not fid or not newname or not oldname:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
       # 连接数据库 - 使用路径参数，确保每个目录有自己的数据库
        sqlite_conn = get_sqlite_conn(corpid, path)
        if not sqlite_conn:
            return JSONResponse(content={"code": 500, "msg": "数据库连接失败"})
        
        try:
            cursor = sqlite_conn.cursor()
            
            # 检查文件是否存在
            cursor.execute("SELECT * FROM file_info WHERE fid = ? AND name = ?", (fid, oldname))
            file_info = cursor.fetchone()
            
            if not file_info:
                return JSONResponse(content={"code": 404, "msg": "file not exist"})    
            
            cursor.execute("UPDATE file_info SET name = ? WHERE fid = ? AND name = ?",
                          (newname, fid, oldname))

            sqlite_conn.commit()            
            # 更新MySQL
            mysql_conn = get_mysql_conn()
            if mysql_conn:
                try:
                    mysql_cursor = mysql_conn.cursor()
                    mysql_cursor.execute(f"UPDATE file_info_{corpid} SET NAME = %s WHERE fid = %s AND name = %s",
                                        (newname, fid, oldname))
                    mysql_conn.commit()
                except Exception as e:
                    logger.error(f"MySQL更新失败: {e}")
                finally:
                    try:
                        mysql_conn.close()
                    except Exception as e:
                        logger.warning(f"关闭MySQL连接失败: {e}")
            
            return JSONResponse(content={"code": 200})
        except Exception as e:
            print(f"文件重命名失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
        finally:
            sqlite_conn.close()
    
    elif action == "delete":
        # 文件删除（移动到回收站）
        if not corpid or not subid or not uid or not fid or not path or not name:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})       
        sqlite_conn = get_sqlite_conn(corpid, path)
        if not sqlite_conn:
            return JSONResponse(content={"code": 500, "msg": "数据库连接失败"})        
        try:
            cursor = sqlite_conn.cursor()
            cursor.execute("SELECT uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath FROM file_info WHERE fid = ? AND name = ?", (fid, name))
            file_info = cursor.fetchone()
            if not file_info:
                return JSONResponse(content={"code": 404, "msg": "file not exist"})            

            # 移动到回收站  fid,name,uid,vpath,pid,nodeid,fsize,cursize,filetype,ctime,filepath
            current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            # 拼接SQL插入语句
            insertsql = """
                INSERT INTO file_info (fid,name,uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath,vpath,mtime)
                VALUES ('{}', '{}', {}, {}, '{}', '{}', {}, '{}', '{}', '{}', '{}', '{}')
            """.format(fid, name, file_info[1], file_info[2], file_info[3], file_info[4], file_info[5], file_info[6], file_info[7], file_info[8], path, current_time)
            
            ret = nas_add_file(corpid, RECYBIN_PATH, fid, name, insertsql)
            
            if ret != 0:
                return JSONResponse(content={"code": 500, "msg": "回收站插入错误"})            
            cursor.execute("DELETE FROM file_info WHERE fid = ? AND name = ?", (fid, name))            
            sqlite_conn.commit()
                   
            # 更新MySQL
            mysql_conn = get_mysql_conn()
            if mysql_conn:
                try:
                    mysql_cursor = mysql_conn.cursor()
                    mysql_cursor.execute(f"update file_info_{corpid} set delflag = 1 WHERE fid = %s AND name = %s", (fid, name))
                    mysql_conn.commit()
                except Exception as e:
                    logger.error(f"MySQL更新失败: {e}")
                finally:
                    try:
                        mysql_conn.close()
                    except Exception as e:
                        logger.warning(f"关闭MySQL连接失败: {e}")
            
            return JSONResponse(content={"code": 200})
        except Exception as e:
            print(f"文件删除失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
        finally:
            sqlite_conn.close()
    
    elif action == "move":
        # 文件移动
        if not corpid or not subid or not uid or not fid or not name or not oldpath or not newpath:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
        
        # 连接数据库 - 使用路径参数，确保每个目录有自己的数据库
        sqlite_conn = nas_sqlite_dirdb_get(corpid, oldpath)
        if not sqlite_conn:
            return JSONResponse(content={"code": 500, "msg": "数据库连接失败"})
        
        try:
            cursor = sqlite_conn.cursor()
            
            # 查询文件信息
            cursor.execute("SELECT uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath FROM file_info WHERE fid = ? AND name = ?", (fid, name))
            file_info = cursor.fetchone()
            
            if not file_info:
                return JSONResponse(content={"code": 404, "msg": "file not exist"})
        
            # 复制到新路径
            current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            # 拼接SQL插入语句
            insertsql = """
                INSERT INTO file_info (fid,name,uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath,vpath,mtime)
                VALUES ('{}', '{}', {}, {}, '{}', '{}', {}, '{}', '{}', '{}', '{}', '{}')
            """.format(fid, name, file_info[0], file_info[1], file_info[2], file_info[3], file_info[4], file_info[5], file_info[6], file_info[7], newpath, current_time)
            
            # 插入到新路径
            ret = nas_add_file(corpid, newpath, fid, name, insertsql)
            
            if ret != 0:
                return JSONResponse(content={"code": 500, "msg": "复制到新路径失败"})
            
            # 从原路径删除
            cursor.execute("DELETE FROM file_info WHERE fid = ? AND name = ?", (fid, name))
            
            sqlite_conn.commit()
            
            # 更新MySQL
            mysql_conn = get_mysql_conn()
            if mysql_conn:
                try:
                    mysql_cursor = mysql_conn.cursor()
                    mysql_cursor.execute(f"UPDATE file_info_{corpid} SET fpath = %s, vpath = %s WHERE fid = %s AND name = %s",
                                        (newpath, newpath, fid, name))
                    mysql_conn.commit()
                except Exception as e:
                    logger.error(f"MySQL更新失败: {e}")
                finally:
                    try:
                        mysql_conn.close()
                    except Exception as e:
                        logger.warning(f"关闭MySQL连接失败: {e}")
            
            return JSONResponse(content={"code": 200})
        except Exception as e:
            print(f"文件移动失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
        finally:
            sqlite_conn.close()
    
    elif action == "copy":
        # 文件复制
        if not corpid or not subid or not uid or not fid or not name or not oldpath or not newpath:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
        
        sqlite_conn = nas_sqlite_dirdb_get(corpid)
        if not sqlite_conn:
            return JSONResponse(content={"code": 500, "msg": "数据库连接失败"})
        
        try:
            cursor = sqlite_conn.cursor()
            cursor.execute("SELECT uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath FROM file_info WHERE fid = ? AND name = ?", (fid, name))
            file_info = cursor.fetchone()
            
            if not file_info:
                return JSONResponse(content={"code": 404, "msg": "file not exist"})
        
            # 移动到新路径
            current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            # 拼接SQL插入语句
            insertsql = """
                INSERT INTO file_info (fid,name,uid,pid,nodeid,fsize,cursize,filetype,ctime,filepath,vpath,mtime)
                VALUES ('{}', '{}', {}, {}, '{}', '{}', {}, '{}', '{}', '{}', '{}', '{}')
            """.format(fid, name, file_info[0], file_info[1], file_info[2], file_info[3], file_info[4], file_info[5], file_info[6], file_info[7], newpath, current_time)
            
            # 插入到新路径
            ret = nas_add_file(corpid, newpath, fid, name, insertsql)
            
            if ret != 0:
                return JSONResponse(content={"code": 500, "msg": "移动到新路径失败"})
            
            return JSONResponse(content={"code": 200})
        except Exception as e:
            print(f"文件复制失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部错误"})
        finally:
            sqlite_conn.close()
    
    elif action == "getmetainfo":
        # 获取文件详细信息
        if not fid or not corpid:
            return JSONResponse(content={"code": 400, "msg": "缺少必要参数"})
        
        try:
            file_info = fidcache.get_fileinfo(corpid, fid)
            if not file_info:
                return JSONResponse(content={"code": 404, "msg": "文件不存在"})
            
            # 构建响应内容，优先使用解析后的元数据
            response_content = {
                "name": file_info.get("name", ""),
                "fsize": file_info.get("fsize", 0),
                "mimeid": file_info.get("mimeid", 0),
                "mtime": file_info.get("mtime", 0),
                "mimetype": "",  # 简化实现，实际应该根据文件类型确定
                "ftype": file_info.get("ftype", ""),
                "codetime": file_info.get("codetime", ""),
                "vcode": file_info.get("vcode", ""),
                "profile": file_info.get("profile", ""),
                "resolvepower": file_info.get("resolvepower", ""),
                "vbit_rate": file_info.get("vbit_rate", ""),
                "display_ratio": file_info.get("display_ratio", ""),
                "bittype": file_info.get("bittype", ""),
                "pix_fmt": file_info.get("pix_fmt", ""),
                "b_frames": file_info.get("b_frames", ""),
                "frame_rate": file_info.get("frame_rate", ""),
                "level": file_info.get("level", ""),
                "acode": file_info.get("acode", ""),
                "sample_rate": file_info.get("sample_rate", ""),
                "abit_rate": file_info.get("abit_rate", ""),
                "channel": file_info.get("channel", "")
            }
            
            return JSONResponse(content=response_content)
        except Exception as e:
            print(f"获取文件详细信息失败: {e}")
            return JSONResponse(content={"code": 500, "msg": "内部服务错误"})
    
    else:
        return JSONResponse(content={"code": 400, "msg": "无效的操作类型"})

# 视频抽帧接口
@file_router.get("/v1/oss/vodpic")
async def get_video_frame(
    fid: str = Query(...),
    corpid: str = Query(...),
    timeoff: Optional[int] = Query(2),
    width: Optional[int] = Query(200),
    height: Optional[int] = Query(400)
):
    try:
        # 查询fidcache获取文件信息
        file_info = get_fid_cache(fid, corpid)
        if not file_info:
            return JSONResponse(content={"code": 404, "msg": "文件不存在"})         
        fpath = file_info.get('fpath', '')
        if not fpath:
            return JSONResponse(content={"code": 400, "msg": "文件路径无效"})        
        # 解析文件路径
        parts = fpath.split(':', 1)
        if len(parts) != 2:
            return JSONResponse(content={"code": 400, "msg": "文件路径格式错误"})        
        nodeid = parts[0]
        edge_path = parts[1]
        # 获取节点信息
        node_info = getnodeip(nodeid)
        if not node_info:
            return JSONResponse(content={"code": 500, "msg": "无法获取节点信息"})
        # 构建重定向URL
        ip = node_info['netip']
        port = node_info['port']
        mimeid = file_info.get('mimeid', 0)
        typeid = 7  # 假设7表示视频类型
        redirect_url = f"http://{ip}:{port}/file?action=getpic&fid={fid}&path={edge_path}&type={typeid}&width={width}&height={height}&mid={mimeid}&timeoff={timeoff}"
        return RedirectResponse(url=redirect_url, status_code=302)
        
    except Exception as e:
        print(f"视频抽帧失败: {e}")
        return JSONResponse(content={"code": 500, "msg": "内部错误"})

# 生成缩略图接口
@file_router.get("/v1/oss/thumbnail")
async def generate_thumbnail(
    fid: str = Query(...),
    corpid: str = Query(...),
    subid: str = Query(...),
    uid: str = Query(...),
    width: Optional[int] = Query(100),
    height: Optional[int] = Query(120)
):
    try:
        # 查询fidcache获取文件信息
        file_info = get_fid_cache(fid, corpid)
        if not file_info:
            return JSONResponse(content={"code": 404, "msg": "文件不存在"})
        
        fpath = file_info.get('fpath', '')
        if not fpath:
            return JSONResponse(content={"code": 400, "msg": "文件路径无效"})
        
        # 解析文件路径
        parts = fpath.split(':', 1)
        if len(parts) != 2:
            return JSONResponse(content={"code": 400, "msg": "文件路径格式错误"})
        
        nodeid = parts[0]
        edge_path = parts[1]
        
        # 获取节点信息
        node_info = getnodeip(nodeid)
        if not node_info:
            return JSONResponse(content={"code": 500, "msg": "无法获取节点信息"})
        
        # 构建重定向URL
        ip = node_info['netip']
        port = node_info['port']
        mimeid = file_info.get('mimeid', 0)
        # 这里假设文件类型为图片，可以根据实际情况调整typeid
        typeid = 2  # 假设2表示图片类型
        
        # 构建符合C语言版本的重定向URL
        redirect_url = f"http://{ip}:{port}/file?action=downthumbnail&fid={fid}&path={edge_path}&type={typeid}&width={width}&height={height}&mid={mimeid}"
        
        # 返回302重定向
        return RedirectResponse(url=redirect_url, status_code=302)
        
    except Exception as e:
        print(f"生成缩略图失败: {e}")
        return JSONResponse(content={"code": 500, "msg": "内部错误"})